/* 
 * File:   TableTranscriber.cpp
 * Author: Guilherme Monteiro
 * 
 */

#include "../headers/TableTranscriber.h"


/**
 * Constructor of class table transcriber.
 * 
 * @param lowResolutionImagePath
 * @param model
 * @param rotatedTag
 */
TableTranscriber::TableTranscriber(string lowResolutionImagePath, GenericModel* model, string rotatedTag) {
    this->metaInfo = new TranscriberMetaInfo(lowResolutionImagePath, model);
    this->model = model;
    setRotation(rotatedTag);
}

TableTranscriber::TableTranscriber(const TableTranscriber& orig) {
}

TableTranscriber::~TableTranscriber() {
}

/**
 * Run the table transcriber. If the image
 * have a table rotated in 90 degrees, a VerticalTableHandler
 * is created and new images with rotation corrected, such as
 * a new MetaInfo, now with the new low resolution image path.
 * Therefore, it run normally with the metainfo created in constructor.
 */
void TableTranscriber::run() throw (NullImageException) {
    if(this->isRotated) {
        try {
            VerticalTableHandler* handler = 
                    new VerticalTableHandler(this->metaInfo->getLowResolutionImagePath(), 
                    this->metaInfo->getHighResolutionImagePath());
            
            vector<string> newPaths = handler->rotateImageInNinetyDegrees();
            
            this->metaInfo->setLowResolutionImagePath(newPaths[0]);
            this->metaInfo->setHighResolutionImagePath(newPaths[1]);
        }
        catch(NullImageException &nie) {
            throw nie;
        }
    }
    
    vector<ROI*>::iterator it_rois;
    
    VectorROIsCreator* vRoisCreator = new VectorROIsCreator(this->metaInfo, this->isRotated);
    vRoisCreator->createROIs();
    vector<ROI*> roisVector = vRoisCreator->getVectorOfROIs();
    //printf("\nroisVector.size: %d\n", roisVector.size());
    
    for(it_rois = roisVector.begin(); it_rois != roisVector.end(); it_rois++ ) {
        Heuristic* heuristic = new Heuristic(*it_rois, this->metaInfo, this->model);
        vector< vector<Line> > linesTranscribed = heuristic->transcribeLines();
        
        // linesTranscribed[0] == vertical lines
        // linesTranscribed[1] == horizontal lines
        //printf("vertical size: %d\n", linesTranscribed[0].size());
        //printf("horizontal size: %d\n", linesTranscribed[1].size());
        
//        for(int i = 0; i < linesTranscribed[0].size(); i++) {
//            printf("background vertical: (%d,%d)-(%d,%d)\n",
//                    linesTranscribed[0][i].p1.x, 
//                    linesTranscribed[0][i].p1.y, 
//                    linesTranscribed[0][i].p2.x, 
//                    linesTranscribed[0][i].p2.y);
//        }
//        
//        for(int j = 0; j < linesTranscribed[1].size(); j++) {
//            printf("background horizontal: (%d,%d)-(%d,%d)\n",
//                    linesTranscribed[1][j].p1.x, 
//                    linesTranscribed[1][j].p1.y, 
//                    linesTranscribed[1][j].p2.x, 
//                    linesTranscribed[1][j].p2.y);
//        }
        
        IplImage* img = cvLoadImage(metaInfo->getHighResolutionImagePath().c_str());
        
        for(int i = 0; i < linesTranscribed.size(); i++) {
            for(int j = 0; j < linesTranscribed[i].size(); j++) {
                CvPoint p1 = linesTranscribed[i][j].p1;
                CvPoint p2 = linesTranscribed[i][j].p2;
                cvLine(img, p1, p2, CV_RGB(255,0,0));
            }
        }
        
//        cvShowImage("image", img);
//        cvResizeWindow("image", 600, 600);
//        cvNamedWindow("image");
//        cvWaitKey(0);
//        cvDestroyAllWindows();
        
        CellsCreator* cCreator = new CellsCreator(linesTranscribed[0], linesTranscribed[1]);
        vector<Cell> cellsTranscribed = cCreator->createCells();
        
//        for(int i = 0; i < cells.size(); i++) {
//            printf("cells %d: (%d,%d)-(%d,%d)\n", i+1, 
//                    cells[i].leftUpper.x, cells[i].leftUpper.y, 
//                    cells[i].rightBottom.x, cells[i].rightBottom.y);
//        }
        
//        int cont = 0;
//        for(int i = 0; i < cells.size(); i++) {
//            if(cells[i].leftUpper.x != -1 && cells[i].leftUpper.y != -1 &&
//                    cells[i].rightBottom.x != -1 && cells[i].rightBottom.y != -1)
//                cont++;
//        }
//        printf("count cells: %d\n", cont);
        
//        printf("cells array size: %d\n", cellsTranscribed.size());
        
//        FalsePositiveRemover* remover = new FalsePositiveRemover(*it_rois, cells);
//        vector<Cell> validLines = remover->removeInvalidBorders();

//--------------------------------------------------------------------------------        
//        vector<Line> tempLinesTranscribed;
//        vector< vector<Line> >::iterator it1;
//        vector<Line>::iterator it2;
//        for(it1 = linesTranscribed.begin(); it1 != linesTranscribed.end(); it1++) {
//            for(it2 = (*it1).begin(); it2 != (*it1).end(); it2++) {
//                tempLinesTranscribed.push_back(*it2);
//            }
//        }
//-----------------------------------------------------------------------------------        
        
        GenericAdapter* genericAdapter = new GenericAdapter(HIGH_2_LOW, this->metaInfo);
        vector<Cell> cellsTranscribedAndAdapted = genericAdapter->adaptCells(cellsTranscribed);
        
        createModelPreview(*it_rois, cutAndSaveLowImage(*it_rois), cellsTranscribedAndAdapted);
        
//        printf("cells.size: %d\n", (int)cellsTranscribedAndAdapted.size());
        
//        for(int i = 0; i < cellsFiltered.size(); i++) {
//            printf("cells %d: (%d,%d)-(%d,%d)\n", i+1, 
//                    cellsFiltered[i].leftUpper.x, cellsFiltered[i].leftUpper.y, 
//                    cellsFiltered[i].rightBottom.x, cellsFiltered[i].rightBottom.y);
//        }
//        for(int i = 0; i < cellsTranscribedAndAdapted.size(); i++) {
//            printf("cells %d: (%d,%d)-(%d,%d)\n", i+1, 
//                    cellsTranscribedAndAdapted[i].leftUpper.x, cellsTranscribedAndAdapted[i].leftUpper.y, 
//                    cellsTranscribedAndAdapted[i].rightBottom.x, cellsTranscribedAndAdapted[i].rightBottom.y);
//        }
        
        FileWriter* fileWriter = new FileWriter(metaInfo->getOutputFilePath());
        
        if((*it_rois)->getIndexROI() == 0) {
            fileWriter->writeHeader1(genericAdapter->adaptPoint((*it_rois)->getLeftUpper()),
                        genericAdapter->adaptPoint((*it_rois)->getRightBottom()));
        }
        else {
            fileWriter->writeHeaderN(genericAdapter->adaptPoint((*it_rois)->getLeftUpper()),
                        genericAdapter->adaptPoint((*it_rois)->getRightBottom()));
        }
        
        fileWriter->writeCells(cellsTranscribedAndAdapted);
        
        (*it_rois)->releaseROI();
        
        //printf("---------------------------------------------------------------\n\n");
    }
}

/**
 * Create preview of models from the table
 * in low resolution and save it in <modelPreview>
 * directory.
 * 
 * @param roi
 * @param cellsFiltered
 */
void TableTranscriber::createModelPreview(ROI* roi, IplImage* lowResolutionTable, vector<Cell> cellsFiltered) {
//     string p = (roi->getLowResolutionImageCutPath()).c_str();
        
//    printf("%s\n", p.c_str());
//    IplImage* img = cvLoadImage(p.c_str());
    for(int i = 0; i < cellsFiltered.size(); i++) {
//            printf("left: (%d,%d)-(%d,%d)\n", 
//                    cellsFiltered[i].left.p1.x, 
//                    cellsFiltered[i].left.p1.y,
//                    cellsFiltered[i].left.p2.x,
//                    cellsFiltered[i].left.p2.y);
        cvLine(lowResolutionTable, cellsFiltered[i].left.p1, 
                cellsFiltered[i].left.p2,
                CV_RGB(255,0,0));

//            printf("up: (%d,%d)-(%d,%d)\n", 
//                    cellsFiltered[i].up.p1.x, 
//                    cellsFiltered[i].up.p1.y,
//                    cellsFiltered[i].up.p2.x,
//                    cellsFiltered[i].up.p2.y);
        cvLine(lowResolutionTable, cellsFiltered[i].up.p1, 
                cellsFiltered[i].up.p2,
                CV_RGB(255,0,0));

//            printf("right: (%d,%d)-(%d,%d)\n", 
//                    cellsFiltered[i].right.p1.x, 
//                    cellsFiltered[i].right.p1.y,
//                    cellsFiltered[i].right.p2.x,
//                    cellsFiltered[i].right.p2.y);
        cvLine(lowResolutionTable, cellsFiltered[i].right.p1, 
                cellsFiltered[i].right.p2,
                CV_RGB(255,0,0));

//            printf("down: (%d,%d)-(%d,%d)\n", 
//                    cellsFiltered[i].down.p1.x, 
//                    cellsFiltered[i].down.p1.y,
//                    cellsFiltered[i].down.p2.x,
//                    cellsFiltered[i].down.p2.y);
        cvLine(lowResolutionTable, cellsFiltered[i].down.p1, 
                cellsFiltered[i].down.p2,
                CV_RGB(255,0,0));
    }  
//    cvShowImage("image", lowResolutionTable);
//    cvResizeWindow("image", lowResolutionTable->width, lowResolutionTable->height);
//    cvNamedWindow("image");
//    cvWaitKey(0);
//    cvDestroyAllWindows();
    
    ImageSaver saver(lowResolutionTable, roi->getModelPreviewPath());
    saver.save();
    
    cvReleaseImage(&lowResolutionTable);
}

/**
 * Sets the atribute isRotated, that
 * indicates if the image has a table
 * rotated in 90 degrees.
 * 
 * @param rotatedTag
 */
void TableTranscriber::setRotation(string rotatedTag) throw (InvalidRotatedTagException) {
    if(strcmp(rotatedTag.c_str(), "-r") == 0) {
        this->isRotated = true;
    }
    else if(strcmp(rotatedTag.c_str(), "-nr") == 0) {
        this->isRotated = false;
    }
    else {
        throw InvalidRotatedTagException();
    }
}

/**
 * Cut the low resolution image
 * and save it in 'tabelasBaixa' folder.
 * 
 * @param r: roi
 * 
 * @return the low resolution table
 */
IplImage* TableTranscriber::cutAndSaveLowImage(ROI* r) throw (NullImageException) {
    IplImage* lowResolutionImage = cvLoadImage(r->getOriginalLowResolutionImagePath().c_str());
    
    if(lowResolutionImage == 0) {
        throw NullImageException(r->getOriginalLowResolutionImagePath());
    }
    
    vector<CvPoint> vecOfPoints;
    vecOfPoints.push_back(r->getLeftUpper());
    vecOfPoints.push_back(r->getRightBottom());
    
    GenericAdapter* adapter = new GenericAdapter(HIGH_2_LOW, this->metaInfo);
    vecOfPoints = adapter->adaptPoints(vecOfPoints);
    
    CvPoint leftUpperAdapted = vecOfPoints.at(0);
    CvPoint rightBottomAdapted = vecOfPoints.at(1);
    
    try {
        ImageCutter* cutter = new ImageCutter(lowResolutionImage, leftUpperAdapted, rightBottomAdapted);
        cutter->cut();
    }
    catch(InvalidRectException &ire) {
        cout << "Your entry to this table is invalid. To be valid it need be a rectangle. The program will be closed." << endl;
        exit(1);
    }
    
    ImageSaver saver(lowResolutionImage, r->getLowResolutionImageCutPath());
    saver.save();
    
    return lowResolutionImage;
}